//超时重发，异步回调，timer监听超时问，高效利用带宽

const crc = require("../../utils/crc");
var utils = require("../../utils/util.js");
var crcUtils = require("../../utils/crc.js");
var console_log = require("../../utils/logUtils.js");
var bluedManage = require("./blue");

const header = 0xAA; //链路层协议报文头标识
var ntr = 20; //必须应答的最大报文个数 最大39
var send_ntr = 0; //已经发送的 ntr数
var rto = 200; //应答超时时间
var mcp = 20; //可连续发生的ntr个数 最大20 
var receiver_ntr_count = 0; //连续收到的报文个数
var receiverBlueEmsData = [] //接收到的蓝牙报文队列
var receiverEmsData = [] //接收到的报文队列
var receiverCurrentPackLenght = 0; //已接收报文的长度
var receiver_cmd = 0; //接收到的命令号
var send_array = []; //待发送数据报文
var rto_send_package = 0; //rto 定时发送数量

var linkTimer;
const app = getApp();
const defaultByteLength = 16;
const packeByteLength = 20;// 导入所需的模块

var todo_send_blue_data = [] //数据发送队列
var _applicationWatch; //应用层监听器
var lastSendData = [] //上次发送数据的缓存
var lastSendResult = true; //上次是否发送成功
//链路层自定的code值
var LinkCode = {
  properties: {
    0: {
      name: "成功",
      value: [0]
    },
    1: {
      name: "重置链路失败",
      value: [1]
    },
    2: {
      name: "配置NRT失败",
      value: [2]
    },
  }
}
/**
 * 链路层报文封装了
 * @param 0:data 1:命令 isCmd
 * @param 序列号 seq
 * @param 数据值 data
 */
function link_package(cmd, data) {
  let isCmd = cmd
  if (cmd == 1) {
    isCmd = 128
  }
  console.log("isCmd:" + isCmd + " data:" + data)
  let totalData = [];
  var length = data.length;
  var data_len_20 = length / defaultByteLength;
  data_len_20 = Math.trunc(data_len_20);
  var data_len_0 = length % defaultByteLength;
  var i = 0;
  if (data_len_20 > 0) {
    for (; i < data_len_20; i++) {
      let perData = [];
      let tmpSeq = 0
      if (cmd == 0) {
        tmpSeq = app.getLinkedSeq()
      }
      var cmdId = isCmd + tmpSeq % 128;
      console.log("cmdId1:" + cmdId + "  seq1:" + tmpSeq);
      perData = perData.concat(cmdId)
      let da = data.slice(defaultByteLength * i, defaultByteLength * (i + 1));
      perData = perData.concat(da)
      totalData = totalData.concat(header)
      totalData = totalData.concat(mergLinkHeadAndCrc(perData));
    }
  }
  if (data_len_0 > 0) {
    var ed = data.slice(
      defaultByteLength * i,
      defaultByteLength * i + data_len_0
    );
    let perData = [];
    let tmpSeq = 0
    if (cmd == 0) {
      tmpSeq = app.getLinkedSeq()
    }
    var cmdId = isCmd + tmpSeq % 128;
    console.log("cmdId2:" + cmdId + "  seq2:" + tmpSeq);
    perData = perData.concat(cmdId)
    let emptyData = Array(defaultByteLength - data_len_0).fill(0xFF);
    perData = perData.concat(ed)
    perData = perData.concat(emptyData)
    totalData = totalData.concat(header)
    totalData = totalData.concat(mergLinkHeadAndCrc(perData));
  }
  return totalData;
}
//组装最后的报文
function mergLinkHeadAndCrc(sendData) {
  let data = sendData.concat()
  let totalData = []
  totalData = totalData.concat(data)
  var hexStrWithBank = utils.byteArrayToHex(totalData);
  var crcCalcu = parseCrc(hexStrWithBank);
  var crcByte = utils.HexStrToByte(crcCalcu);
  console_log.log(
    "data bank:" +
    utils.ByteArrayToHexBankStr(totalData) +
    "--carc:" +
    utils.byteArrayToHex(crcByte)
  );
  var afterCrcBytes = [];
  afterCrcBytes = afterCrcBytes.concat(totalData);
  afterCrcBytes = afterCrcBytes.concat(crcByte);
  return afterCrcBytes;
}

//设置 NTR
function configNTR() {
  let sendData = Array(16).fill(0xFF);
  sendData[0] = 1
  sendData[1] = ntr
  sendData[2] = rto
  sendData[3] = mcp
  let data = link_package(1, sendData)
  sendBlueData(data)
}

//解析 NTR应答
function analysisNTR(ntrResult) {
  if (!ntrResult) return;
  let resetResult = false
  if (ntrResult[1] == 1) {
    console.log("配置重置报文成功")
    resetResult = true;
  }
  if (_applicationWatch) {
    _applicationWatch(resetResult ? LinkCode.properties[0].value : LinkCode.properties[2].value)
  }
}

function sendAck(seq) {
  let sendData = Array(16).fill(0xFF);
  sendData[0] = 0
  sendData[1] = 0
  sendData[2] = seq
  sendData[3] = 0
  // let data = link_package(1, sendData)
  let header = [0x88]
  let totalData = []
  totalData = totalData.concat(header)
  totalData = totalData.concat(sendData)
  let readlData = mergLinkHeadAndCrc(totalData)
  let endData = [0xAA]
  endData = endData.concat(readlData)
  sendBlueDataQueue(true, endData)
}


//查询链路层设置报文
function queryLinkPackage() {
  let sendData = [20]
  sendData[0] = 3
  link_package(cmd1, sendData)
}

//解析链路层应答报文的解析
function analysisAckLink(linkPackage) {
  if (!linkPackage) return;
  if (linkPackage[1] == 0) {
    console.log("解析链路层协议出错")
    return false
  }
  let bandArr = linkPackage.slice(4, 7);
  let crcTag = linkPackage[5]
  ntr = linkPackage[9]
  rto = linkPackage[10]
  mcp = linkPackage[8]
}

//发送重置链路指令报文
function sendRestLinkPackage() {
  resetReceiverData()
  app.setSeq(0);
  let sendData = Array(16).fill(0xFF)
  sendData[0] = 5
  sendData[1] = 0
  sendData[2] = 40
  sendData[3] = 1
  app.setLinkedSeq(0);
  var linkPackage = link_package(1, sendData);
  console.log("重置链路报文：" + utils.byteArrayToHex(linkPackage));
  sendBlueData(linkPackage)
  send_ntr = 0;
}

//解析应答重置链路报文

function analysisAckRestPackage(resetAckPackage) {
  if (resetAckPackage[1] == 0) {
    console.log("解析应答重置链路报文错误")
    if (_applicationWatch) {
      _applicationWatch(LinkCode.properties[1].value)
    }
    return false
  }
  let bandArr = resetAckPackage.slice(2, 5)
  let crcTag = resetAckPackage[6]
  console.log("重置链路报文成功")
  configNTR()
}

/**
 *开启链接超时监听
 */
function startWatchLinked(watch) {
  linkTimer = setTimeout(watch, rto)
}

/**
 * 重置链接超时监听
 */
function restartWatchLinked() {
  if (!linkTimer) {
    clearTimeout(linkTimer);
  }
  startWatchLinked();
}

/**
 * 停止链接监听
 */
function stopWatchLinked() {
  if (!linkTimer) {
    clearTimeout(linkTimer);
  }
}

/**
 * 链路层数据报文的处理
 * @param receiverData
 */
function handleLinkData(receiverData) {
  let data0 = receiverData[0]
  switch (data0) {
    case 0:
      //收到报文应答
      linkReceiverAck(receiverData)
      break
    case 2:
      //接收到NTR应答报文
      analysisNTR(receiverData);
      break;
    case 3:
      break;
    case 4:
      //接收到链路层重置
      analysisAckRestPackage(receiverData)
      break;
    case 5:
      break;
    case 6:
      //接收到重置链路请求的返回报文
      analysisAckRestPackage(receiverData)
      break;
  }
}

/**
 *处理待发送数据集
 */
function waitSendData(sendData) {
  if (!sendData) return;
  for (var i = 0; i < sendData.length; i += 16) {
    send_array.push(sendData.slice(i, i + 16));
  }
}
//blue 连接
function startBlueConnect(deviceId) {
  bluedManage.startBlueConnect(deviceId, watchReceiverData);
}
//调用蓝牙发送报文
function sendBlueData(data) {
  bluedManage.sendBlueData(data)
}
//监听回调
function watchReceiverData(data) {
  console.log("link receiver data:" + data)
  if (data == 1) {
    //蓝牙连接成功后重置链路
    sendRestLinkPackage()
  } else {
    //接收到数据的处理
    // handleReceiverData(data)
    let blueData = utils.HexStrToByte(data)
    receiverBlueEmsData = receiverBlueEmsData.concat(blueData)
    let length = receiverBlueEmsData.length;
    var data_len_20 = length / packeByteLength;
    data_len_20 = Math.trunc(data_len_20);
    var i = 0;
    if (data_len_20 > 0) {
      for (; i < data_len_20; i++) {
        var da = receiverBlueEmsData.slice(packeByteLength * i, packeByteLength * (i + 1));
        handleReceiverData(da)
      }
      receiverBlueEmsData = receiverBlueEmsData.slice(packeByteLength * data_len_20, receiverBlueEmsData.length)
      console_log.log("last data:", utils.byteArrayToHex(receiverBlueEmsData))
    }
  }
}
//链路层 处理接收到的数据
function handleReceiverData(receiverBlueData) {
  let blueData = receiverBlueData.concat()
  let currentTime = Date.now();
  if (!blueData || blueData.length < 2 || blueData[0] != 0xAA) return; //接收到空数据返回
  if (blueData[0] == 0xAA && blueData.length >= 20) {
    //验证单条接收的报文的crc过程
    let beforeCrcData = blueData.slice(1, blueData.length - 2)
    let crcData = blueData.slice(blueData.length - 2, blueData.length)
    console.log("收到数据 crc：" + crcData)
    var hexStrWithBank = utils.byteArrayToHex(beforeCrcData);
    var crcCalcu = parseCrc(hexStrWithBank);
    var calce = utils.HexStrToByte(crcCalcu);
    console.log("收到数据计算的 crc：" + calce)
  }
  receiver_ntr_count++;
  checkReceiverNtr(receiver_ntr_count)
  let cmdByte = blueData[1]
  if (cmdByte >= 128) {
    //链路层数据
    handleLinkData(blueData.slice(2, blueData.length - 2))
  } else {
    //应用层报文的处理
    if (receiverEmsData.length < 1) {
      //应用层首包数据 取出报文长度
      let lenghtByte = blueData.slice(4, 6)
      let lenght = utils.bytesToLong(lenghtByte);
      receiverCurrentPackLenght = lenght + 8
      if (receiverCurrentPackLenght > 16) {
        let appDate = blueData.slice(2, 18)
        receiverEmsData = receiverEmsData.concat(appDate);
      } else {
        let appDate = blueData.slice(2, receiverCurrentPackLenght + 2)
        receiverEmsData = receiverEmsData.concat(appDate);
      }

    } else {
      let lastLength = receiverCurrentPackLenght - receiverEmsData.length
      if (lastLength > 14) {
        //剩余的报文长度大于14个
        let appDate = blueData.slice(2, 18)
        receiverEmsData = receiverEmsData.concat(appDate);
      } else {
        //剩余的报文长度小于14个
        let appDate = blueData.slice(2, lastLength + 2)
        receiverEmsData = receiverEmsData.concat(appDate);
      }
    }
    //判断已接收的报文数据长度和报文长度是否相同
    if (receiverCurrentPackLenght == receiverEmsData.length) {
      //接收到完整的报文内容
      let realData = receiverEmsData.slice(0, receiverEmsData.length - 2)
      let calce = parseCrc(realData);
      console.log("数据的计算的CRC：" + calce)
      let appDateCrc = receiverEmsData.slice(receiverEmsData.length - 2, receiverEmsData.length)
      console.log("数据的接收的CRC：" + appDateCrc)
      if (_applicationWatch) {
        let endData = realData.concat()
        _applicationWatch(endData)
      }
      receiver_cmd = cmdByte;
      resetReceiverData()
    }
  }
}
//发送请求下一个报文序号
function sendNextPackeIndex(cmdByte) {
  //表示应用层数据 处理获取下个期望的序号
  let cmdByte7 = (cmdByte + 1) % 128;
  console.log("期望下一个序号::" + cmdByte7)
  sendAck(cmdByte7);
}
//重置接收缓存
function resetReceiverData() {
  receiverEmsData = [];
  console.log("重置接收 receiver");
}
// 解析crc
function parseCrc(e) {
  if (!e) return null;
  let crc = crcUtils.ToCRC16(e, false);
  console.log("crc 计算+" + crc);
  return crc;
}

//将数据放入到队列中
function sendBlueDataQueue(header, e) {
  console.log("放入到队列中的数据：", utils.byteArrayToHex(e))
  if (header) {
    if (todo_send_blue_data.length > 2 && todo_send_blue_data[0] == 0xAA && todo_send_blue_data[1] == 0x88) {
      console.log("应答报文已存在 发送队列")
      return
    }
    let tmpData = e;
    tmpData = tmpData.concat(todo_send_blue_data);
    todo_send_blue_data = tmpData;
  } else {
    todo_send_blue_data = todo_send_blue_data.concat(e);
  }
}
//定时发送处理
function sendBlueTimer(mtu) {
  let tmpSendData = todo_send_blue_data.slice(0, mtu)
  // console.log("sendBlueTimer 1")
  if (!lastSendResult) {
    console.log("上次数据包发送失败，重新发送", utils.byteArrayToHex(lastSendData))
    tmpSendData = lastSendData
    // console.log("sendBlueTimer 2")
  } else {
    todo_send_blue_data = todo_send_blue_data.slice(mtu, todo_send_blue_data.length)
    // console.log("sendBlueTimer 3")
  }
  lastSendData = tmpSendData;
  // console.log("sendBlueTimer 4",lastSendData.length)
  if (lastSendData.length > 0) {
    send_ntr++;
    console.log("ntr value", ntr, "send_ntr", send_ntr)
    if (send_ntr <= ntr) {
      sendBlueData(lastSendData)
      console.log("sendBlueTimer 5")
    } else {
      console.log("sendBlueTimer 6")
    }
  }
}
//设置应用层监听器
function setApplicationWatch(appWatch) {
  _applicationWatch = appWatch;
}
//收到报文的应答
function linkReceiverAck(data) {
  let operator = data[1]
  let seq = data[2]
  if (operator == 1) {
    console.log("存在缺失报文")//没有重传
    lastSendResult = false;
  } else {
    lastSendResult = true
  }
}
//根据ntr 判断是否发送报文
function checkReceiverNtr(ntr_count) {
  send_ntr = 0;
  rto_send_package = 0;
  if (ntr_count > (ntr + 1) / 2) {
    console.log("接收到一半ntr时候应答")
    sendNextPackeIndex(receiver_cmd)
    receiver_ntr_count = 0
  }
}
//处理ntr 定时器时间
function doLinktimer() {
  rto_send_package++;
  console.log("rto_send_package", rto_send_package)
  sendNextPackeIndex(receiver_cmd);
}

module.exports = {
  sendRestLinkPackage: sendRestLinkPackage,
  startWatchLinked: startWatchLinked,
  sendApplicationData: link_package,
  handleLinkData: handleLinkData,
  ntr: ntr,
  startBlueConnect: startBlueConnect,
  sendBlueData: sendBlueData,
  sendBlueDataQueue: sendBlueDataQueue,
  sendBlueTimer: sendBlueTimer,
  setApplicationWatch: setApplicationWatch,
  LinkCode: LinkCode,
  linkRto: rto, //应答超时时间
  linkdoLinktimer: doLinktimer,

};